package view;

import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import language.Strings;
import model.DataNormalizate;
import model.OperacoesArquivos;

import org.encog.engine.network.activation.ActivationSigmoid;
import org.encog.engine.util.Format;
import org.encog.mathutil.randomize.NguyenWidrowRandomizer;
import org.encog.neural.data.NeuralDataPair;
import org.encog.neural.data.NeuralDataSet;
import org.encog.neural.networks.BasicNetwork;
import org.encog.neural.networks.layers.BasicLayer;
import org.encog.neural.networks.logic.FeedforwardLogic;
import org.encog.neural.networks.training.lma.LevenbergMarquardtTraining;
import org.encog.persist.EncogPersistedCollection;
import org.jdom.JDOMException;

import util.Constantes;
import util.PersistenciaXml;
import util.Util;
import bean.Configuracao;
import javax.swing.JRadioButton;
import javax.swing.JCheckBox;

public class StartTrain extends JFrame {

	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private JLabel lbOperacao = null;
	private JLabel lbOperacaoTitulo = null;
	private JLabel lbErroAtual = null;
	private JLabel lbErroAtualTitulo = null;
	private JLabel lbEpoch = null;
	private JLabel lbEpochTitulo = null;
	private JLabel lbErroEsperado = null;
	private JLabel lbErroEsperadoTitulo = null;
	private JLabel lbStatus = null;
	private JLabel lbStatusTitulo = null;
	private JButton btSalvar = null;
	private JButton btExecutar = null;
	private JPanel jPanelTitulo = null;

	private static int EPOCH;
	private static int HIDDEN_COUNT;
	private static double Erro;
	
	private static boolean executando = false;
	TrainNetwork train = null;
	private JCheckBox chkGerarArquivoParaTreinamento = null;
	public StartTrain() {
		super();
		initialize();
	}

	private void initialize() {
		this.setSize(473, 294);
		this.setContentPane(getJContentPane());
		this.setTitle(Strings.TITULO_TREINAMENTO);
		this.setResizable(false);
		this.setDefaultCloseOperation(HIDE_ON_CLOSE);
		this.setLocation(
						((Toolkit.getDefaultToolkit().getScreenSize().width / 2) - (this
								.getWidth() / 2)),
						((Toolkit.getDefaultToolkit().getScreenSize().height / 2) - (this
								.getHeight() / 2)));
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing (WindowEvent e) {
				Principal.JANELA_ABERTA = false;				
			}			
		});
	}

	private JPanel getJContentPane() {
		if (jContentPane == null) {
			lbStatusTitulo = new JLabel();
			lbStatusTitulo.setBounds(new Rectangle(160, 210, 126, 15));
			lbStatusTitulo.setText("");
			lbStatus = new JLabel();
			lbStatus.setBounds(new Rectangle(17, 210, 130, 15));
			lbStatus.setText(Strings.TEXT_LABEL_STATUS);
			lbErroEsperadoTitulo = new JLabel();
			lbErroEsperadoTitulo.setBounds(new Rectangle(160, 180, 126, 15));
			lbErroEsperadoTitulo.setText("");
			lbErroEsperado = new JLabel();
			lbErroEsperado.setBounds(new Rectangle(17, 180, 130, 15));
			lbErroEsperado.setText(Strings.TEXT_LABEL_ERRO_ESPERADO + ":");
			lbEpochTitulo = new JLabel();
			lbEpochTitulo.setBounds(new Rectangle(160, 150, 127, 15));
			lbEpochTitulo.setText("");
			lbEpoch = new JLabel();
			lbEpoch.setBounds(new Rectangle(17, 150, 130, 15));
			lbEpoch.setText(Strings.TEXT_LABEL_EPOCH + ":");
			lbErroAtualTitulo = new JLabel();
			lbErroAtualTitulo.setBounds(new Rectangle(160, 120, 126, 15));
			lbErroAtualTitulo.setText("");			
			lbErroAtual = new JLabel();
			lbErroAtual.setBounds(new Rectangle(17, 120, 130, 15));
			lbErroAtual.setText(Strings.TEXT_LABEL_ERRO_ATUAL + ":");
			jContentPane = new JPanel();
			jContentPane.setLayout(null);
			jContentPane.add(lbErroAtual, null);
			jContentPane.add(lbErroAtualTitulo, null);
			jContentPane.add(lbEpoch, null);
			jContentPane.add(lbEpochTitulo, null);
			jContentPane.add(lbErroEsperado, null);
			jContentPane.add(lbErroEsperadoTitulo, null);
			jContentPane.add(lbStatus, null);
			jContentPane.add(lbStatusTitulo, null);
			jContentPane.add(getBtSalvar(), null);
			jContentPane.add(getBtExecutar(), null);
			jContentPane.add(getJPanelTitulo(), null);
			jContentPane.setBackground(Color.WHITE);
			jContentPane.add(getChkGerarArquivoParaTreinamento(), null);
		}
		return jContentPane;
	}

	private JButton getBtSalvar() {
		if (btSalvar == null) {
			btSalvar = new JButton();
			btSalvar.setBounds(new Rectangle(330, 170, 110, 25));
			btSalvar.setText(Strings.TEXT_BT_SALVAR);
			btSalvar.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if(train == null){
						JOptionPane.showMessageDialog(null, Strings.TEXT_LABEL_TREINAR_MSG);
						return;
					}
					
					Object[] options = {Strings.TEXT_OP_SIM, Strings.TEXT_OP_NAO};
					
					int resp = JOptionPane.showOptionDialog(null, Strings.TEXT_LABEL_PERGUNTA_SALVAR, 
							Strings.TITULO_JOPTION_PANE, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, 
							null, options, options[0]);
					
					if(resp == JOptionPane.YES_OPTION){
						File trainedFile = Constantes.TRAINED_NETWORK_FILE_EG;
						
						EncogPersistedCollection encog = new EncogPersistedCollection(trainedFile);
						encog.create();
						encog.add(Constantes.TRAINED_NETWORK_NAME, train.getNetwork());
						
						String mensagem = Strings.TEXT_LABEL_ARQUIVOS_SALVOS_MSG + "\n\n";
						
						JOptionPane.showMessageDialog(null, mensagem + trainedFile.getAbsolutePath(),
								Strings.TITULO_JOPTION_PANE, JOptionPane.NO_OPTION);
					}
				}
			});
		}
		return btSalvar;
	}

	private JButton getBtExecutar() {
		if (btExecutar == null) {
			btExecutar = new JButton();
			btExecutar.setBounds(new Rectangle(330, 125, 110, 25));
			btExecutar.setText(Strings.TEXT_BT_EXECUTAR);
			btExecutar.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					carregaDados();
					
					eventoTreinar();
				}
			});
		}
		return btExecutar;
	}

	private void eventoTreinar(){
		if(executando == false){
			executando = true;
			File arquivoTreinamento = Constantes.PERFIL_TRAIN;
			
			if(getChkGerarArquivoParaTreinamento().isSelected()){
				arquivoTreinamento = geraArquivoParaTreinamento();
			}
			
			if(!arquivoTreinamento.exists()){
				return;
			}
				
			if(!Constantes.MAXIMOS_CSV.exists()){
				System.err.println("Arquivo maximos nao encontrado! Gerando agora...");
				gerarMaximosDoArquivoDeTreinamento(arquivoTreinamento);
//				return;
			}
			
			getBtExecutar().setText(Strings.TEXT_BT_PARAR);						
			
			if(train == null){
				train = new TrainNetwork();	
			}
			
			train.setFile(arquivoTreinamento);
			train.setIndex(1);
			Thread thread = new Thread(train);
			thread.start();
			
		} else{
			executando = false;
			getBtExecutar().setText(Strings.TEXT_BT_EXECUTAR);
			
			if(train != null){
				train.setIndex(EPOCH);
			}
		}
	}
	
	private void gerarMaximosDoArquivoDeTreinamento(File file) {
		List<String> conteudoFile = OperacoesArquivos.lerTxt(file.getAbsolutePath());
		int[] maximos = new int[9];
		String linha = "";
		
		for (String string : conteudoFile) {
			String[] cortado = string.split(",");
			
			for (int i = 0; i < cortado.length; i++) {
				int item = Integer.parseInt(cortado[i]);
				
				if(item > maximos[i]){
					maximos[i] = item;
				}
			}
		}
		
		for (int i = 0; i < maximos.length; i++) {
			linha += maximos[i];
			
			if(i < maximos.length-1){
				linha += ",";
			}
		}
		
		System.out.println(linha);
		OperacoesArquivos.salvaDadosArquivoTexto(linha, Constantes.MAXIMOS_CSV.getAbsolutePath(), false);
	}

	private JPanel getJPanelTitulo() {
		if (jPanelTitulo == null) {
			jPanelTitulo = new JPanel();
			jPanelTitulo.setLayout(null);
			jPanelTitulo.setBounds(new Rectangle(0, 0, 473, 86));
			lbOperacaoTitulo = new JLabel();
			lbOperacaoTitulo.setText(Strings.TEXT_BT_CONFIG_TREINAMENTO);
			lbOperacaoTitulo.setBounds(new Rectangle(160, 25, 132, 25));
			lbOperacaoTitulo.setFont(new Font("Arial", Font.BOLD, 18));
			lbOperacaoTitulo.setForeground(Color.WHITE);
			lbOperacao = new JLabel();
			lbOperacao.setText("");
			lbOperacao.setBounds(new Rectangle(17, 25, 130, 25));
			lbOperacao.setFont(new Font("Arial", Font.BOLD, 18));
			lbOperacao.setForeground(Color.WHITE);
			jPanelTitulo.add(lbOperacao, null);
			jPanelTitulo.add(lbOperacaoTitulo, null);
			jPanelTitulo.setBackground(new Color(35,35,142));
		}
		return jPanelTitulo;
	}

	public File geraArquivoParaTreinamento(){
		File perfilTrain = Constantes.PERFIL_TRAIN;
		File diretorioDadosColetados = Constantes.DIRETORIO_PERFIL_CONSISTENCIA_COLETA;
		String[] arquivosColetados = diretorioDadosColetados.list();
		
		List<String> conteudoTrain = new ArrayList<String>();
		List<String> conteudoArquivo;
		File arquivoCorrente;
				
		if(arquivosColetados != null){
			for (String nomeDoArquivo : arquivosColetados) {				
				arquivoCorrente = new File(diretorioDadosColetados, nomeDoArquivo);
				if(arquivoCorrente.isFile() && nomeDoArquivo.startsWith("E")){
					conteudoArquivo = OperacoesArquivos.lerTxt(arquivoCorrente.getAbsolutePath());
					
					for (String item : conteudoArquivo) {
						conteudoTrain.add(item);
					}
					
					conteudoArquivo = null;
				}
				
				arquivoCorrente = null;
			}
		} else{
			System.err.println("Nao eh um diretorio!");
		}
		
		if(conteudoTrain.size() == 0){
			String erroParte1 = Strings.TEXT_LABEL_DIR_VAZIO_MSG + ":\n" + diretorioDadosColetados.getAbsolutePath();
						
			JOptionPane.showMessageDialog(null, erroParte1, Strings.TEXT_LABEL_DIR_VAZIO, JOptionPane.ERROR_MESSAGE);
		}
		
		if(perfilTrain.exists()){
			perfilTrain.delete();
		} 
		
		for (String item : conteudoTrain) {
			OperacoesArquivos.salvaDadosArquivoTexto(item, perfilTrain.getAbsolutePath(), true);
		}	
		
		return perfilTrain;
	}
	
	public void carregaDados(){
		PersistenciaXml persistenciaXml = new PersistenciaXml();
		
		try {
			Configuracao config = persistenciaXml.selectConfiguracoesXML();
			
			EPOCH = Integer.parseInt(config.getEpoch());
			HIDDEN_COUNT = Integer.parseInt(config.getQtdNeuroniosCamadaEscondida());
			Erro = Double.parseDouble(config.getErro()) / 100;			
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private class TrainNetwork implements Runnable{
		File arquivoTreinamento;
		int index = 1;
		private BasicNetwork network = null;
		
		public BasicNetwork generateNetwork(){
			ActivationSigmoid sigmoid = new ActivationSigmoid();		
			boolean limiar = true;
			
			BasicNetwork network = new BasicNetwork();
			network.addLayer(new BasicLayer(sigmoid, limiar, Constantes.INPUT_COUNT));
			network.addLayer(new BasicLayer(sigmoid, limiar, HIDDEN_COUNT));
			network.addLayer(new BasicLayer(sigmoid, limiar, Constantes.OUTPUT_COUNT));
			network.setLogic(new FeedforwardLogic());
			network.getStructure().finalizeStructure();
			network.reset();		
	
			(new NguyenWidrowRandomizer(-1, 1)).randomize(network);
			network.setBiasActivation(1.0);
			
			return network;
		}
		
		private void imprime(NeuralDataSet dataSet){
			for(NeuralDataPair pair : dataSet){
				for (int i = 0; i < dataSet.getInputSize(); i++) {
					System.out.print(pair.getInput().getData(i) + ", ");
				}
				System.out.println();
			}
		}
		
		public void train() {			
			BasicNetwork network = generateNetwork();
			DataNormalizate norm = new DataNormalizate();
			
			NeuralDataSet trainingSet = Util.loadDataCSV(getArquivoTreinamento());
//			NeuralDataSet dadaSet = Util.loadDataCSV(Constantes.MAXIMOS_CSV);
//			int[] maxValues = norm.getMaxValues(dadaSet);
			int[] maxValues = OperacoesArquivos.lerMaximos(Constantes.MAXIMOS_CSV.getAbsolutePath());			
			norm.dataNormalizate(trainingSet, maxValues);
				
//			imprime(trainingSet);
			
			final LevenbergMarquardtTraining train = new LevenbergMarquardtTraining(network, trainingSet);
//			final Train train = new LevenbergMarquardtTraining(network, trainingSet);
//			final Train train = new Backpropagation(network, trainingSet);
			
			lbStatusTitulo.setForeground(Color.GREEN);
			lbStatusTitulo.setText(Strings.TEXT_LABEL_EXECUTANDO);
			lbErroEsperadoTitulo.setText(String.valueOf(Format.formatPercent(Erro)));
			getBtSalvar().setEnabled(false);
			
			do {			
				train.iteration();
				
				lbEpochTitulo.setText(String.valueOf(index));
				lbErroAtualTitulo.setText(String.valueOf(Format.formatPercent(train.getError())));
				
				index++;
//			} while ((index < EPOCH) && (train.getError() > Erro));
			} while ((index < EPOCH));
	
			executando = false;
			getBtSalvar().setEnabled(true);
			getBtExecutar().setText(Strings.TEXT_BT_EXECUTAR);
			lbStatusTitulo.setText(Strings.TEXT_LABEL_FINALIZADO);
			lbStatusTitulo.setForeground(Color.RED);
			setNetwork(network);
		}

		public void setNetwork(BasicNetwork network){
			this.network = network;
		}
		
		public BasicNetwork getNetwork(){
			return network;
		}
		
		public void setIndex(int index){
			this.index = index;
		}
		
		public void setFile(File arquivoTreinamento){
			this.arquivoTreinamento = arquivoTreinamento;
		}
		
		public File getArquivoTreinamento() {
			return arquivoTreinamento;
		}
		
//		public int getIndex(){
//			return this.index;
//		}
		
		@Override
		public void run() {
			train();
		}
	}

	/**
	 * This method initializes chkGerarArquivoParaTreinamento	
	 * 	
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getChkGerarArquivoParaTreinamento() {
		if (chkGerarArquivoParaTreinamento == null) {
			chkGerarArquivoParaTreinamento = new JCheckBox();
			chkGerarArquivoParaTreinamento.setBounds(new Rectangle(320, 94, 136, 21));
			chkGerarArquivoParaTreinamento.setText("Gerar Arquivo");
			chkGerarArquivoParaTreinamento.setSelected(true);
			chkGerarArquivoParaTreinamento.setToolTipText("Gera novamente o arquivo para o treinamento da RNA.");
			chkGerarArquivoParaTreinamento.setBackground(Color.WHITE);
		}
		return chkGerarArquivoParaTreinamento;
	}
}  //  @jve:decl-index=0:visual-constraint="10,10"